#include "key_process.h"

static int cur_pos = 0; 	// record the cursor position 
static int tmp_line_nr = -1;	// keep trace of the line number that current cmd in history file
static int cur_line_nr = -1;	// record the current cmd line_nr in history file

/* count the history file's lines */
int count_lines(const char *filename) {
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
		fprintf(stderr, "key_process error: count_lines\n");
        return -1;
    }

    int count = 0;
    char ch;

    while ((ch = fgetc(file)) != EOF) {
        if (ch == '\n') {
            count++;
        }
    }

    fclose(file);
    return count;
}

/* read specific line in history_cmd file */
char *read_specific_line(int line_num) 
{
	char *line_buf = (char *) malloc(sizeof(char) * CMD_LINE_BUFSIZE);
	if (!line_buf) {
		return NULL;
	}

	// FILE *file = fopen("/home/winterz/Project/joky/.history", "r"); // read only
	FILE *file = fopen(CMD_HST_FILE, "r"); // read only
	if (!file) {
		free(line_buf);
		return NULL;
	}

	int cur_line = 0;
	while (fgets(line_buf, CMD_LINE_BUFSIZE, file) != NULL) {
		cur_line++;

		if (cur_line == line_num) {
			fclose(file);
			return line_buf;
		}
	}

	fclose(file);
	free(line_buf);
	return NULL;
}

/* redraw the buffer */
void draw_input(const char *buf, int cursor_dist_to_r, const char *_prompt, const char *_dir)
{
	/* draw input */
	printf(HIDE_CUR); 	// hide cursor to refresh smoothly 
	printf(CLL);
	printf(MOVE_CUR_BL);
	printf("%s %s", _prompt, _dir);
	printf("%s", buf);
	for (int i = cursor_dist_to_r; i > 0; i--) 	// cursor_dist_to_r means the distance between cursor and right edge
		printf(LEFT);
	printf(DISPLAY_CUR);

}

/* history command display */
void handle_command_navigation(char *buf, int *position, int direction) {

    if (direction == -1 && tmp_line_nr > 1) {
        tmp_line_nr--;  // Move to previous command
    } else if (direction == 1 && tmp_line_nr < cur_line_nr) {
        tmp_line_nr++;  // Move to next command
    }

    char *cmd = read_specific_line(tmp_line_nr);
    if (cmd == NULL)
        return;

    // Update the buffer with the command
    int len = strlen(cmd) - 1; // Do not need \n
    strncpy(buf, cmd, len);
    free(cmd);
    buf[len] = '\0';
    *position = len;
    cur_pos = *position;
}

/* autocomplete for tab keypress */
void autocomplete(int *pos, char *buf, char *res)
{
	// find the word after the last space or just this word if it doesn't have space
	buf[*pos] = '\0';
	int len;
	const char *last_space = strrchr(buf, ' ');	
	if (last_space == NULL) { 
		strncpy(res, buf, *pos);
		res[*pos] = '\0';
	} else if ((last_space - buf) + 1 == *pos) {
		res[0] = '\0';
		return;
	} else {
		len = *pos - (last_space - buf) - 1;
		strncpy(res, last_space + 1, len); 
		res[len] = '\0';
	}

	// handle the result, catched by above steps
	// two parts: path and file (which would be completed), 
	// split by the last '/'(included in path), just like 'path/file'
	const char *last_slash = strrchr(res, '/');	
	int path_len, file_len;
	if (last_slash == NULL) {
		path_len = 2; // default is './'
		file_len = len; // the whole string is file
	} else {
		path_len = (last_slash - res) + 1; // 1 for '/'
		file_len = len - (last_slash - res) - 1;
	}

	char *path = (char *) malloc(sizeof(char) * (path_len + 1)); // 1 for null terminator
	char *file = (char *) malloc(sizeof(char) * (file_len + 1)); // 1 for null terminator
	if (!path || !file) {
		fprintf(stderr, "\nkey_process error: autocomplete()-->malloc\n");	
		free(path);
		free(file);
		return;
	}

	if (last_slash == NULL) {
		strncpy(path, "./", path_len);
		strncpy(file, res, file_len);
	} else {
		strncpy(path, res, path_len);
		strncpy(file, res + path_len, file_len);
	}
	path[path_len] = '\0';
	file[file_len] = '\0';

	// complete the matched file
	// printf("\n\t%sPATH: %s    FILE: %s%s\n", YELLOW_HL, path, file, RESET);
	DIR *dir = opendir(path);
	if (!dir) {
		fprintf(stderr, "\nkey_process error: autocomplete()-->opendir\n");	
		free(path);
		free(file);
		return;
	}

	direntry_t *de = get_list_files(path);
	if (de == NULL) {
		fprintf(stderr, "key_process error: autocomplete()-->get_list_files");
		free(path);
		free(file);
		free(de);
		return;
	}

	int count_entry = 0;
	int unique_match_idx = -1;
	for (int i = 0; de[i].file_type != -1; i++) {
		if (strstr(de[i].name, file) == de[i].name) {
	 		count_entry++;
			unique_match_idx = (count_entry == 1) ? i : -1;
		}
	}

	// printf("\n%d\n", unique_match_idx);
	if (count_entry == 1) {
		// complete the path in buffer
		*pos = *pos - strlen(file); // go back to the copy start 
		const char *match_file = de[unique_match_idx].name;
		int len = strlen(match_file);
		strncpy(&buf[*pos], match_file, len);

		*pos += len;
		// printf("%d", de[unique_match_idx].file_type);
		if (de[unique_match_idx].file_type == T_DIR) {
			buf[*pos] = '/';
			(*pos)++;
		} 

		buf[*pos] = '\0';
		cur_pos = *pos;
	}

	if (file[0] != '\0' && count_entry > 1) {
		printf("\n");
		print_list_files(de, 1, file);
	}

	// free allocated memory
	free(path);
	free(file);
	free(de);
}

int key_process(int c, char *buf, int *position, const char *_prompt, const char *_dir) {

    int end = 0;  // Whether to end processing or modify the buffer later
	int cursor_dist_to_r;
	int history_lines = count_lines(CMD_HST_FILE);

    if (tmp_line_nr == -1) {
        tmp_line_nr = history_lines + 1; 
    }
    cur_line_nr = history_lines + 1;

    switch (c) {
        case LF:
        case CR:
			/* newline */
			end = 1;
            printf("\r\n");
			cur_pos = 0;
			cur_line_nr++;
			tmp_line_nr = cur_line_nr;
            break;
        case TAB:
			/* auto completion */
			if (*position == 0)
				break;
			char res[FILEPATH_SIZE];
			autocomplete(position, buf, res);
			// if (res[0] != '\0')
			// 	printf("\ntab: '%s'\n", res);

			cursor_dist_to_r = *position - cur_pos;
			buf[*position] = '\0';
			draw_input(buf, cursor_dist_to_r, _prompt, _dir);
			break;
        case BACKSPACE:
			/* back */
            if (*position > 0 && cur_pos == *position) {
                printf("\b \b"); 		// Move back, print space, and move back again
                (*position)--;
				cur_pos--;
                buf[*position] = '\0'; 	// Remove the last character from the buffer
            } else if (cur_pos > 0 && cur_pos != *position) {
				/* updata buf */
				cursor_dist_to_r = *position - cur_pos;
				memcpy(&buf[cur_pos - 1], &buf[cur_pos], cursor_dist_to_r);
				if (*position > 0)
					(*position)--;
				if (cur_pos > 0)
					cur_pos--;
				buf[*position] = '\0';

				/* draw input */
				draw_input(buf, cursor_dist_to_r, _prompt, _dir);
			}
            break;
		case CTRL_KEY('l'):
			/* clean screen */
			printf(CLS);  	// ANSI escape code for clearing the screen
			buf[*position] = '\0'; 	// keep the current input 
			printf("%s %s%s", _prompt, _dir, buf);
			break;
		case CTRL_KEY('u'):
			/* clean current input */
    		printf(CLL);  			// ANSI escape code to clear the entire line
			printf(MOVE_CUR_BL); 	// ANSI escape code to move the cursor to beginning of current line
			printf("%s %s", _prompt, _dir);
			*position = 0;
			buf[*position] = '\0';
			cur_pos = 0;
			break;
		case CTRL_KEY('p'):
			/* previous command */
			handle_command_navigation(buf, position, -1);
			buf[*position] = '\0';

			// draw the previous cmmand
			 cursor_dist_to_r = *position - cur_pos;
			draw_input(buf, cursor_dist_to_r, _prompt, _dir);
			break;
		case CTRL_KEY('n'):
			/* next command */
			handle_command_navigation(buf, position, 1);
			buf[*position] = '\0';

			// draw the previous cmmand
			cursor_dist_to_r = *position - cur_pos;
			draw_input(buf, cursor_dist_to_r, _prompt, _dir);
			break;
		case CTRL_KEY('f'):
			/* move cursor to right */
			if (cur_pos < *position) {
				cur_pos++;
				printf(RIGHT);
			}
			break;
		case CTRL_KEY('b'):
			/* move cursor to left */
			if (cur_pos > 0) {
				cur_pos--;
				printf(LEFT);
			}
			break;
		case CTRL_KEY('d'):
			/* delete a character */
			if (cur_pos >= 0 && *position > cur_pos) {
				/* update buf */
				cursor_dist_to_r = *position - cur_pos;
				memcpy(&buf[cur_pos], &buf[cur_pos + 1], cursor_dist_to_r);
				(*position)--;
				if (cur_pos > 0)
					cur_pos--;
				if (cur_pos == 0)
					cursor_dist_to_r -= 1; 		// cur_pos == 0 means cur not move back, but position move back, so cursor_dist_to_r minus 1
				buf[*position] = '\0';

				/* draw input */
				draw_input(buf, cursor_dist_to_r, _prompt, _dir);
			}
			break;
		case CTRL_KEY('a'):
			int count1 = cur_pos - 0; 
			cur_pos = 0;
			while (count1--) {
				printf(LEFT);
			}
			break;
		case CTRL_KEY('e'):
			int count2 = *position - cur_pos;
			cur_pos = *position;
			while (count2--) {
				printf(RIGHT);
			}
			break;
        default:
			/* ignore the escape sequence */
			static int skip_esc = 0;
			if (c == '\033') 
				skip_esc = 3;
			if (skip_esc != 0) {
				skip_esc--;
				return end;
			}

			/* normally display character */
            if (isprint(c)) {
				cursor_dist_to_r = *position - cur_pos;
				if (cursor_dist_to_r > 0) {
					/* update buf */
					memcpy(&buf[cur_pos + 1], &buf[cur_pos], cursor_dist_to_r);
					buf[cur_pos++] = c;
                	(*position)++;
					buf[*position] = '\0';

					/* draw input */
					draw_input(buf, cursor_dist_to_r, _prompt, _dir);
				} else {
					putchar(c);
					buf[*position] = c;
					(*position)++;
					cur_pos++;
				}

            }
			skip_esc = 0;
            break;
    }
    fflush(stdout);

	return end;
}
